/* -*-c++-*- IfcGears - www.ifcgears.com  - Copyright (C) 2011 Fabian Gerold
 *
 * This library is open source and may be redistributed and/or modified under  
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or 
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 * OpenSceneGraph Public License for more details.
*/

#include <osgDB/Registry>
#include <osgDB/ReadFile>
#include <osgDB/ReaderWriter>
#include <osgDB/FileNameUtils>
#include <osg/Material>
#include <osg/Depth>
#include <osg/Point>
#include <osg/ShapeDrawable>
#include <osg/BlendFunc>
#include <osg/CullFace>

#include <utility>
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#ifdef IFCGEARS_OPENMP
#include <omp.h>
#endif

#include "carve/carve.hpp"
#include "carve/csg.hpp"
#include "carve/csg_triangulator.hpp"
#include "carve/faceloop.hpp"
#include "carve/geom3d.hpp"
#include "carve/input.hpp"
#include "carve/polyhedron_base.hpp"
#include "carve/poly.hpp"
#include "carve/polyhedron_base.hpp"
#include "carve/poly.hpp"
#include "carve/triangulator.hpp"
#include "carve/common/geometry.hpp"
#include "carve/common/rgb.hpp"

#include "ifcgears/IFC2X4/include/IfcProduct.h"
#include "ifcgears/IFC2X4/include/IfcProject.h"
#include "ifcgears/IFC2X4/include/IfcElement.h"
#include "ifcgears/IFC2X4/include/IfcRelVoidsElement.h"
#include "ifcgears/IFC2X4/include/IfcRelAggregates.h"
#include "ifcgears/IFC2X4/include/IfcRelContainedInSpatialStructure.h"
#include "ifcgears/IFC2X4/include/IfcFeatureElementSubtraction.h"
#include "ifcgears/IFC2X4/include/IfcRepresentation.h"
#include "ifcgears/IFC2X4/include/IfcProductRepresentation.h"
#include "ifcgears/IFC2X4/include/IfcPropertySet.h"
#include "ifcgears/IFC2X4/include/IfcPropertySetDefinition.h"
#include "ifcgears/IFC2X4/include/IfcRelDefinesByProperties.h"
#include "ifcgears/IFC2X4/include/IfcSpace.h"
#include "ifcgears/IFC2X4/include/IfcBeam.h"
#include "ifcgears/IFC2X4/include/IfcCurtainWall.h"
#include "ifcgears/IFC2X4/include/IfcWindow.h"
#include "ifcgears/IFC2X4/include/IfcBuildingStorey.h"
#include "ifcgears/IFC2X4/include/IfcSite.h"
#include "ifcgears/IFC2X4/include/IfcLengthMeasure.h"

#include "ifcgears/model/IfcGearsModel.h"
#include "ifcgears/model/IfcGearsException.h"
#include "ifcgears/reader/IfcStepReader.h"
#include "ifcgears/writer/IfcStepWriter.h"
#include "ifcgears/model/UnitConverter.h"

#include "RepresentationConverter.h"
#include "PlacementConverter.h"
#include "Carve2OsgConverter.h"
#include "ReaderWriterIFC.h"

ReaderWriterIFC::ReaderWriterIFC()
{
	osgDB::ReaderWriter::supportsExtension("ifc","Industry Foundation Classes");
	osgDB::ReaderWriter::supportsExtension("stp","Step");
	m_ifc_model		= shared_ptr<IfcGearsModel>(new IfcGearsModel());
	m_step_reader	= shared_ptr<IfcStepReader>(new IfcStepReader());
	m_step_writer	= shared_ptr<IfcStepWriter>(new IfcStepWriter());
	
	m_step_reader->setProgressCallBack( this, &ReaderWriterIFC::slotProgressValueWrapper );
	m_step_reader->setMessageCallBack( this, &ReaderWriterIFC::slotMessageWrapper );
	m_step_reader->setErrorCallBack( this, &ReaderWriterIFC::slotErrorWrapper );

	m_enable_product_color_properties = false;

	osg::Material* material = new osg::Material();
	float shinyness = 35.f;
	material = new osg::Material();
	material->setAmbient( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.02f, 0.025f, 0.03f, 0.1 ) );
	material->setDiffuse( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.8f, 0.82f, 0.84f, 0.1f ) );
	material->setSpecular( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.02f, 0.025f, 0.03f, 0.03f ) );
	material->setShininess( osg::Material::FRONT_AND_BACK, shinyness );
	material->setColorMode( osg::Material::SPECULAR );
	material->setEmission( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.05f, 0.08f, 0.1f, 0.1f ) );
	material->setAlpha(osg::Material::FRONT_AND_BACK, 0.35f );
	m_glass_stateset = new osg::StateSet();
	m_glass_stateset->setMode( GL_BLEND, osg::StateAttribute::ON );
	m_glass_stateset->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
	//m_glass_stateset->setAttribute(new osg::Depth( osg::Depth::LESS, 0.0, 1.0, false));
	m_glass_stateset->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );

	{
		/*
		osg::Material* material = new osg::Material();
		float shinyness = 35.f;
		material = new osg::Material();
		material->setAmbient( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.2f, 0.025f, 0.03f, 0.1 ) );
		material->setDiffuse( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.8f, 0.82f, 0.84f, 0.1f ) );
		material->setSpecular( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.02f, 0.025f, 0.03f, 0.03f ) );
		//material->setShininess( osg::Material::FRONT_AND_BACK, shinyness );
		//material->setColorMode( osg::Material::SPECULAR );
		//material->setEmission( osg::Material::FRONT_AND_BACK, osg::Vec4f( 0.05f, 0.08f, 0.1f, 0.1f ) );
		//material->setAlpha(osg::Material::FRONT_AND_BACK, 0.35f );
		m_heat_stateset = new osg::StateSet();
		m_heat_stateset->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
		m_heat_stateset->setMode( GL_BLEND, osg::StateAttribute::ON );
		m_heat_stateset->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
		m_heat_stateset->setMode( GL_DEPTH_TEST, osg::StateAttribute::ON );
		osg::Depth* depth = new osg::Depth();
		depth->setWriteMask( true );
		m_heat_stateset->setAttributeAndModes( depth, osg::StateAttribute::ON );
		*/



		m_heat_stateset = new osg::StateSet();
		m_heat_stateset->setMode( GL_BLEND, osg::StateAttribute::ON );
		m_heat_stateset->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
		m_heat_stateset->setMode( GL_DEPTH_TEST, osg::StateAttribute::ON );
		m_heat_stateset->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
		osg::ref_ptr<osg::BlendFunc> blend_func = new osg::BlendFunc();
		blend_func->setFunction( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
		m_heat_stateset->setAssociatedModes( blend_func, osg::StateAttribute::ON );
		//stateset_shape->setRenderBinDetails( 100, "RenderBin", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS);

		osg::ref_ptr<osg::CullFace> cull_back = new osg::CullFace( osg::CullFace::BACK );
		m_heat_stateset->setAttributeAndModes( cull_back.get(), osg::StateAttribute::ON );

		//m_heat_stateset->setMode( GL_BLEND, osg::StateAttribute::ON );
		//m_heat_stateset->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
		//m_heat_stateset->setAttribute(new osg::Depth( osg::Depth::LESS, 0.0, 1.0, false));
		//m_heat_stateset->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
	}
}

ReaderWriterIFC::~ReaderWriterIFC()
{
}

void ReaderWriterIFC::setModel( shared_ptr<IfcGearsModel> model )
{
	if( m_ifc_model )
	{
		m_ifc_model->clearIfcModel();
	}
	m_ifc_model = model;

	m_unit_converter = m_ifc_model->getUnitConverter();
	m_representation_converter = shared_ptr<RepresentationConverter>( new RepresentationConverter( m_unit_converter ) );
}

osgDB::ReaderWriter::ReadResult ReaderWriterIFC::readNode(const std::string& filename, const osgDB::ReaderWriter::Options*)
{
	std::string ext = osgDB::getFileExtension(filename);
	if( !acceptsExtension(ext) ) return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
	
	// open file
	std::ifstream infile;
	infile.open ( filename.c_str(), std::ifstream::in);
	
	if( !infile.is_open() )
	{
		return osgDB::ReaderWriter::ReadResult::FILE_NOT_FOUND;
	}

	// get length of file:
	infile.seekg (0, std::ios::end);
	const int length = infile.tellg();
	infile.seekg (0, std::ios::beg);

	// allocate memory:
	std::string buffer( length, '\0' );

	// read data as a block:
	infile.read (&buffer[0],length);
	infile.close();
	
	m_ifc_model->clearIfcModel();
	m_geom_tree.clear();
	
	m_step_reader->readStreamHeader( buffer, m_ifc_model );
	std::string file_schema_version = m_ifc_model->getFileSchema();
	std::map<int,shared_ptr<IfcGearsEntity> > map_entities;
	
	m_err.clear();
	m_err.str( std::string() );

	try
	{
		m_step_reader->readStreamData( buffer, map_entities );
	}
	catch( IfcGearsException& e )
	{
		m_err << e.what();
	}
	catch( std::exception& e )
	{
		m_err << e.what();
	}
	catch( ... )
	{
		m_err << "An error occured in ReaderWriterIFC::readNode";
	}

	try
	{
		// insert entities into model
		std::map<int,shared_ptr<IfcGearsEntity> >::iterator it_model;
		for( it_model=map_entities.begin(); it_model != map_entities.end(); ++it_model )
		{
			shared_ptr<IfcGearsEntity>& ent = (*it_model).second;
				
			try
			{
				m_ifc_model->insertEntity( ent );
			}
			catch( IfcGearsException& e )
			{
				m_err << e.what();
			}

		}

		m_ifc_model->resolveInverseAttributes();
		m_ifc_model->updateCache();

		m_unit_converter = m_ifc_model->getUnitConverter();
		m_representation_converter = shared_ptr<RepresentationConverter>( new RepresentationConverter( m_unit_converter ) );
	}
	catch( IfcGearsException& e )
	{
		m_err << e.what();
	}
	catch( std::exception& e )
	{
		m_err << e.what();
	}
	catch( ... )
	{
		m_err << "An error occured in ReaderWriterIFC::readNode";
	}

	// create geometry for standard IFC2X4 objects
	try
	{
		convertIfcModel();
	}
	catch( IfcGearsException& e )
	{
		m_err << e.what();
	}
	catch( std::exception& e )
	{
		m_err << e.what();
	}
	catch( ... )
	{
		m_err << "An error occured in ReaderWriterIFC::readNode";
	}

	osg::ref_ptr<osg::Group> group_result = new osg::Group();
	group_result->addChild( m_geom_tree.products.get() );
	group_result->addChild( m_geom_tree.terrain.get() );


	const osg::BoundingSphere& bs = group_result->getBound();
	osg::Sphere* sphere = new osg::Sphere( bs.center(), bs.radius() );
	sphere->setName("Bounding_Sphere");
	osg::ShapeDrawable* drawable = new osg::ShapeDrawable( sphere );
	drawable->setName("Bounding_Sphere");
	drawable->setColor( osg::Vec4( 0.7, 0.7, 0.7, 0.1 ) );
	osg::Geode* geode = new osg::Geode();
	geode->setName("Bounding_Sphere");
	geode->addDrawable( drawable );
	geode->setStateSet( m_heat_stateset );
	group_result->addChild( geode );

	computeHeatRays( group_result );

	// osg ReadResult assumes that there is either a valid read object, or an error message
	// however, often there are warnings or errors and still a valid read result
	// this needs to be done via ReaderWriterIFC::getWarnings and ReaderWriterIFC::getErrors
	osgDB::ReaderWriter::ReadResult::ReadStatus status = osgDB::ReaderWriter::ReadResult::FILE_LOADED;
	m_err.seekp(0, std::ios::end);
	std::stringstream::pos_type err_length = m_err.tellp();
	m_err.clear();
	if( err_length > 0 )
	{
		status = osgDB::ReaderWriter::ReadResult::ERROR_IN_READING_FILE;
	}

	return osgDB::ReaderWriter::ReadResult( group_result, status );
}


//#define SHOW_ENTITIES_OUTSIDE_SPATIAL_STRUCTURE

void ReaderWriterIFC::convertIfcModel()
{
	shared_ptr<IfcProject> project = m_ifc_model->getIfcProject();
	if( !project )
	{
		throw IfcGearsException( "ReaderWriterIFC: no valid IfcProject in model." );
	}

	m_product_shapes.clear();
	m_geom_tree.clear();
	std::vector<shared_ptr<IfcProduct> > vec_products;
	std::stringstream err;

	// TODO: adjust carve::EPSILON, EPSILON2 according to units or estimate of model bounds
	carve::EPSILON = 1.4901161193847656e-08;

	const std::map<int,shared_ptr<IfcGearsEntity> >& map = m_ifc_model->getMapIfcObjects();
	std::map<int,shared_ptr<IfcGearsEntity> >::const_iterator it;
	for( it=map.begin(); it!=map.end(); ++it )
	{
		shared_ptr<IfcGearsEntity> obj = (*it).second;

		// TODO: dynamic_cast alternative
		shared_ptr<IfcProduct> product = dynamic_pointer_cast<IfcProduct>( obj );
		if( product )
		{
			vec_products.push_back( product );
		}
	}

	const int num_products = vec_products.size();
	for( int i=0; i<num_products; ++i )
	{
		shared_ptr<IfcProduct> product = vec_products[i];
		const int product_id = product->getId();
		shared_ptr<ProductShape> product_shape( new ProductShape() );

		if( !product->m_Representation )
		{
			continue;
		}

		try
		{
			convertIfcProduct( product, product_shape );
		}
		catch( IfcGearsException& e)
		{
			err << e.what();
		}		
		catch( carve::exception& e)
		{
			err << e.str();
		}
		catch( std::exception& e )
		{
			err << e.what();
		}
		catch( ... )
		{
			err << "error in ReaderWriterIFC::convertIfcRelContainedInSpatialStructure, product id " << product_id << std::endl;
		}

		m_product_shapes[product_id] = product_shape;
		m_processed_products[product_id] = product;
		// progress callback
		double progress = (double)i/(double)num_products;
		if( progress - m_recent_progress > 0.03 )
		{
			// leave 10% of progress to openscenegraph internals
			progressCallback( progress*0.9 );
			m_recent_progress = progress;
		}
	}


	


	// now resolve spatial structure
	ModelGeomTree result_geom;
	try
	{
		convertIfcProject( project, result_geom );
	}
	catch( IfcGearsException& e)
	{
		err << e.what();
	}
	catch(std::exception& e)
	{
		err << e.what();
	}
	catch( ... )
	{
		err << "error in ReaderWriterIFC::convertIfcProject" << std::endl;
	}

	// now process products that are not in the spatial structure


	m_geom_tree.products->addChild( result_geom.products );
	m_geom_tree.storeys->addChild( result_geom.storeys );
	m_geom_tree.spaces->addChild( result_geom.spaces );
	m_geom_tree.terrain->addChild( result_geom.terrain );



	
	float z_shift = 0;
	if( m_representation_converter->getDebugGroupFirst()->getNumChildren() > 0 )
	{
		osg::MatrixTransform* mt = new osg::MatrixTransform( osg::Matrix::translate(0, 0, z_shift) );
		m_geom_tree.products->addChild(mt);
		mt->addChild( m_representation_converter->getDebugGroupFirst() );
	}
	if( m_representation_converter->getDebugGroupSecond()->getNumChildren() > 0 )
	{
		osg::Group* group_second = m_representation_converter->getDebugGroupSecond();
		osg::MatrixTransform* mt = new osg::MatrixTransform( osg::Matrix::translate(0, 0, z_shift) );
		m_geom_tree.products->addChild(mt);
		mt->addChild( group_second );

		//WireFrameModeOn( debug_group_copy );
		group_second->setStateSet(m_glass_stateset);
	}

	progressCallback( 1.0 );

	if( err.tellp() > 0 )
	{
		throw IfcGearsException( err.str().c_str() );
	}
}

void ReaderWriterIFC::convertIfcProject( shared_ptr<IfcProject> project, ModelGeomTree& result_geom )
{
	if( !project )
	{
		return;
	}
	std::string err;
	double progress = 0.0;
	progressCallback( progress );

	// resolve aggregated elements
	std::vector<weak_ptr<IfcRelAggregates> >& vec_decomposed = project->m_IsDecomposedBy_inverse;
	std::vector<weak_ptr<IfcRelAggregates> >::iterator it_rel_dec;

	for( it_rel_dec=vec_decomposed.begin(); it_rel_dec!=vec_decomposed.end(); ++it_rel_dec )
	{
		shared_ptr<IfcRelAggregates> rel( *it_rel_dec );
		try
		{
			convertIfcRelAggregates( rel, result_geom );
		}
		catch( IfcGearsException& e )
		{
			err.append( e.what() );
		}
	}
	if( err.size() > 0 )
	{
		throw IfcGearsException( err );
	}
}



void ReaderWriterIFC::convertIfcRelContainedInSpatialStructure( shared_ptr<IfcRelContainedInSpatialStructure> rel_contained_in_spatial_structure, ModelGeomTree& result_geom )
{
	std::vector<shared_ptr<IfcProduct> >& vec_related_elements = rel_contained_in_spatial_structure->m_RelatedElements;
	std::vector<shared_ptr<IfcProduct> >::iterator it_product;
	std::vector<weak_ptr<IfcRelAggregates> >::iterator it_rel_aggregates;
	std::map<int,shared_ptr<ProductShape> >::iterator it_product_map;
	std::stringstream err;

	for( it_product=vec_related_elements.begin(); it_product!=vec_related_elements.end(); ++it_product )
	{
		shared_ptr<IfcProduct> product = (*it_product);
		if( !product )
		{
			continue;
		}
		const int product_id = product->getId();

		it_product_map = m_product_shapes.find(product_id);
		if( it_product_map != m_product_shapes.end() )
		{
			shared_ptr<ProductShape>& product_group = (*it_product_map).second;

			if( product_group->space_group.valid() )
			{
				result_geom.spaces->addChild( product_group->space_group );
			}
			if( product_group->terrain_group.valid() )
			{
				result_geom.terrain->addChild( product_group->terrain_group );
			}
			if( product_group->product_group.valid() )
			{
				result_geom.products->addChild( product_group->product_group );
			}
		}

		if( dynamic_pointer_cast<IfcSpatialStructureElement>(product) )
		{
			shared_ptr<IfcSpatialStructureElement> spatial_structure_element = dynamic_pointer_cast<IfcSpatialStructureElement>(product);

			// check for Elevation attribute in case of a building storey
			shared_ptr<IfcBuildingStorey> building_storey = dynamic_pointer_cast<IfcBuildingStorey>(product);
			if( building_storey )
			{
				if( building_storey->m_Elevation )
				{
					double elevation = building_storey->m_Elevation->m_value;
					osg::ref_ptr<osg::MatrixTransform> transform_building_storey = new osg::MatrixTransform( osg::Matrix::translate( 0, 0, elevation ) );
					

					// TODO: assign to result transform group
				}

				int building_storey_id = building_storey->getId();
				if( m_building_storeys.find(building_storey_id) == m_building_storeys.end() )
				{
					m_building_storeys[building_storey_id] = shared_ptr<BuildingStoreyGroup>( new BuildingStoreyGroup() );
				}
			}

			if( spatial_structure_element->m_ContainsElements_inverse.size() > 0 )
			{
				std::vector<weak_ptr<IfcRelContainedInSpatialStructure> >& vec_rel_contained = spatial_structure_element->m_ContainsElements_inverse;
				std::vector<weak_ptr<IfcRelContainedInSpatialStructure> >::iterator it_rel_contained;

				// TODO: collect all products for one building storey and then create geometry for products in each storey in parallel

				for( it_rel_contained=vec_rel_contained.begin(); it_rel_contained!=vec_rel_contained.end(); ++it_rel_contained )
				{
					shared_ptr<IfcRelContainedInSpatialStructure> rel( *it_rel_contained );
					try
					{
						convertIfcRelContainedInSpatialStructure( rel, result_geom );
					}
					catch( IfcGearsException& e )
					{
						err << e.what();
					}
					catch( carve::exception& ce )
					{
						err << ce.str();
					}
					catch( std::exception& e )
					{
						err << e.what();
					}
					catch( ... )
					{
						err << "error in ReaderWriterIFC::convertIfcRelContainedInSpatialStructure, product id " << product->getId() << std::endl;
					}
				}
			}
		}

		// further child object related by IfcRelAggregates
		if( product->m_IsDecomposedBy_inverse.size() > 0 )
		{
			std::vector<weak_ptr<IfcRelAggregates> >& vec_rel_aggregates = product->m_IsDecomposedBy_inverse;
			for( it_rel_aggregates=vec_rel_aggregates.begin(); it_rel_aggregates!=vec_rel_aggregates.end(); ++it_rel_aggregates )
			{
				shared_ptr<IfcRelAggregates> rel_aggregates( *it_rel_aggregates );
				try
				{
					convertIfcRelAggregates( rel_aggregates, result_geom );
				}
				catch( IfcGearsException& e )
				{
					err << e.what();
				}
				catch( carve::exception& ce )
				{
					err << ce.str();
				}
				catch( std::exception& e )
				{
					err << e.what();
				}
				catch( ... )
				{
					err << "error in ReaderWriterIFC::convertIfcRelContainedInSpatialStructure, product id " << product->getId() << std::endl;
				}
			}
		}
	}
	if( err.tellp() > 0 )
	{
		throw IfcGearsException( err.str().c_str() );
	}
}

void ReaderWriterIFC::convertIfcRelAggregates( shared_ptr<IfcRelAggregates> rel_aggregates, ModelGeomTree& result_geom )
{
	std::vector<shared_ptr<IfcObjectDefinition> >::iterator				it_rel_obj;
	std::vector<weak_ptr<IfcRelAggregates> >::iterator					it_rel_aggregates;
	std::vector<weak_ptr<IfcRelContainedInSpatialStructure> >::iterator it_rel_contained;
	std::vector<shared_ptr<IfcProduct> >::iterator						it_ifc_product;
	std::map<int,shared_ptr<ProductShape> >::iterator					it_product_map;
	std::stringstream err;

	std::vector<shared_ptr<IfcObjectDefinition> >& vec_related_objects = rel_aggregates->m_RelatedObjects;
	for( it_rel_obj=vec_related_objects.begin(); it_rel_obj!=vec_related_objects.end(); ++it_rel_obj )
	{
		shared_ptr<IfcObjectDefinition> obj_def = (*it_rel_obj);

		// check if object is an IfcProduct and if it has a representation
		if( dynamic_pointer_cast<IfcProduct>(obj_def) )
		{
			shared_ptr<IfcProduct> product = dynamic_pointer_cast<IfcProduct>(obj_def);
			const int product_id = product->getId();
			if( product->m_Representation )
			{
				it_product_map = m_product_shapes.find(product_id);
				if( it_product_map != m_product_shapes.end() )
				{
					shared_ptr<ProductShape>& product_group = (*it_product_map).second;

					if( product_group->space_group.valid() )
					{
						result_geom.spaces->addChild( product_group->space_group );
					}
					if( product_group->terrain_group.valid() )
					{
						result_geom.terrain->addChild( product_group->terrain_group );
					}
					if( product_group->product_group.valid() )
					{
						result_geom.products->addChild( product_group->product_group );
					}
				}
			}
		}

		// further child object related by IfcSpatialStructureElement
		if( dynamic_pointer_cast<IfcSpatialStructureElement>(obj_def) )
		{
			shared_ptr<IfcSpatialStructureElement> spatial_structure_element = dynamic_pointer_cast<IfcSpatialStructureElement>(obj_def);

			if( spatial_structure_element->m_ContainsElements_inverse.size() > 0 )
			{
				std::vector<weak_ptr<IfcRelContainedInSpatialStructure> >& vec_rel_contained = spatial_structure_element->m_ContainsElements_inverse;

				for( it_rel_contained=vec_rel_contained.begin(); it_rel_contained!=vec_rel_contained.end(); ++it_rel_contained )
				{
					shared_ptr<IfcRelContainedInSpatialStructure> rel_contained( *it_rel_contained );
					try
					{
						convertIfcRelContainedInSpatialStructure( rel_contained, result_geom );
					}
					catch( IfcGearsException& e )
					{
						err << e.what();
					}
					catch(std::exception& e)
					{
						err << e.what();
					}
					catch( ... )
					{
						err << "ReaderWriterIFC::convertIfcRelContainedInSpatialStructure failed" << std::endl;
					}
				}
			}
		}

		// further child object related by IfcRelAggregates
		if( obj_def->m_IsDecomposedBy_inverse.size() > 0 )
		{
			std::vector<weak_ptr<IfcRelAggregates> >& vec_rel_aggregates = obj_def->m_IsDecomposedBy_inverse;

			for( it_rel_aggregates=vec_rel_aggregates.begin(); it_rel_aggregates!=vec_rel_aggregates.end(); ++it_rel_aggregates )
			{
				shared_ptr<IfcRelAggregates> rel( *it_rel_aggregates );
				try
				{
					convertIfcRelAggregates( rel, result_geom );
				}
				catch( IfcGearsException& e )
				{
					err << e.what();
				}
				catch(std::exception& e)
				{
					err << e.what();
				}
				catch( ... )
				{
					err << "ReaderWriterIFC::convertIfcRelAggregates failed" << std::endl;
				}
			}
		}
	}
	if( err.tellp() > 0 )
	{
		throw IfcGearsException( err.str().c_str() );
	}
}

void ReaderWriterIFC::slotProgressValueWrapper( void* ptr, double progress_value )
{
	ReaderWriterIFC* myself = (ReaderWriterIFC*)ptr;

	myself->progressCallback( progress_value );
}

void ReaderWriterIFC::slotMessageWrapper( void* ptr, const std::string& str )
{
	ReaderWriterIFC* myself = (ReaderWriterIFC*)ptr;
	if( myself )
	{
		myself->messageCallback( str );
	}
}

void ReaderWriterIFC::slotErrorWrapper( void* ptr, const std::string& str )
{
	ReaderWriterIFC* myself = (ReaderWriterIFC*)ptr;
	if( myself )
	{
		myself->errorCallback( str );
	}
}

// register with Registry to instantiate the above reader/writer.
REGISTER_OSGPLUGIN(ifc, ReaderWriterIFC)
